package online.zhangwenzhe.common.exception;

import online.zhangwenzhe.common.log.LogEntity;
import online.zhangwenzhe.common.log.LogType;
import online.zhangwenzhe.common.trace.RequestWrapper;
import online.zhangwenzhe.common.validation.ValidationException;
import online.zhangwenzhe.common.validation.ValidationResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.client.ResourceAccessException;

import javax.servlet.http.HttpServletRequest;

@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
    protected static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 所有异常报错
     *
     * @param request
     * @param exception
     * @return
     * @throws Exception
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(value = Exception.class)
    public Object allExceptionHandler(HttpServletRequest request,
                                      Exception exception) {
        StringBuilder exceptionInfo = new StringBuilder();
        String lineSeparator = System.getProperty("line.separator");
        exceptionInfo.append("程序异常：");
        exceptionInfo.append(lineSeparator);
        exceptionInfo.append(exception.getLocalizedMessage());
        exceptionInfo.append(lineSeparator);
        exceptionInfo.append(exception.getCause());
        exceptionInfo.append(lineSeparator);
        exceptionInfo.append(exception.getSuppressed());
        exceptionInfo.append(lineSeparator);
        exceptionInfo.append(exception.getMessage());
        exceptionInfo.append(lineSeparator);

        StackTraceElement[] stackTraceElements = exception.getStackTrace();
        for (StackTraceElement element : stackTraceElements) {
            exceptionInfo.append("    " + element.toString());
            exceptionInfo.append(lineSeparator);
        }

        long traceId = 0L;
        if (request instanceof RequestWrapper) {
            RequestWrapper requestWrapper = (RequestWrapper) request;
            traceId = requestWrapper.getId();
        }

        LogEntity logEntity = new LogEntity(traceId, LogType.EXCEPTION, exceptionInfo.toString());

        logger.error(logEntity.toJson());

        ErrorResult<Exception> result =
                new ErrorResult<>(ResultState.SYSTEM_ERROR, "系统发生未知异常", null);

        return result;
    }

    /**
     * 业务异常
     *
     * @param request
     * @param exception
     * @return
     * @throws Exception
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = BusinessException.class)
    public Object businessExceptionHandller(HttpServletRequest request,
                                            BusinessException exception) {
        ErrorResult<BusinessExceptionResult> result =
                new ErrorResult<>(ResultState.BUSINESS_ERROR, exception.getMessage(), exception.getBusinessExceptionResult());
        return result;
    }

    @ExceptionHandler(value = ResourceAccessException.class)
    public Object resourceAccessExceptionHandller(HttpServletRequest request,
                                                  ResourceAccessException exception) {
        return exception.getMessage();
    }

    /**
     * 数据校验异常
     *
     * @param request
     * @param exception
     * @return
     * @throws ValidationException
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = ValidationException.class)
    public Object validationExceptionHandller(HttpServletRequest request,
                                              ValidationException exception) {
        ErrorResult<ValidationResult> result =
                new ErrorResult<>(ResultState.PARAMETER_INVALID, exception.getMessage(), exception.getValidationResult());
        return result;
    }
}
